package cn.lili.modules.kit.plugin.bank;

import cn.lili.common.enums.ResultCode;
import cn.lili.common.event.TransactionCommitSendMQEvent;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.utils.ResultUtil;
import cn.lili.common.utils.SnowFlake;
import cn.lili.common.vo.ResultMessage;
import cn.lili.exchange.AmqpExchangeProperties;
import cn.lili.modules.kit.Payment;
import cn.lili.modules.order.order.client.OrderClient;
import cn.lili.modules.order.order.entity.dos.OrderFlow;
import cn.lili.modules.order.order.entity.enums.ProfitSharingStatusEnum;
import cn.lili.modules.payment.entity.dos.*;
import cn.lili.modules.payment.entity.dto.*;
import cn.lili.modules.payment.entity.enums.OutOrderLogStatusEnums;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.payment.entity.enums.PlatformWalletEnum;
import cn.lili.modules.payment.entity.enums.WalletServiceTypeEnum;
import cn.lili.modules.payment.entity.vo.OutOrderLogVO;
import cn.lili.modules.payment.service.OutOrderDetailLogService;
import cn.lili.modules.payment.service.OutOrderLogService;
import cn.lili.modules.payment.service.PaymentLogService;
import cn.lili.modules.payment.wechat.model.profitsharing.ProfitSharing;
import cn.lili.modules.wallet.service.WalletService;
import cn.lili.routing.PaymentRoutingKey;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * 线下收款
 *
 * @author Chopper
 * @version v1.0 2021-02-20 10:14
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class BankTransferPlugin implements Payment {


    private final ApplicationEventPublisher applicationEventPublisher;


    private final WalletService walletService;

    private final AmqpExchangeProperties amqpExchangeProperties;

    private final RedissonClient redisson;

    private final PaymentLogService paymentLogService;

    private final OutOrderLogService outOrderLogService;

    private final OutOrderDetailLogService outOrderDetailLogService;

    private final OrderClient orderClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(RefundLog refundLog) {
        try {
            refundLog.setIsRefund(true);

            //线下收款的订单统一由平台钱包退款至用户余额，用户可以再次申请提现。
            walletService.increase(
                    UserWalletUpdateDTO.builder()
                            .userId(refundLog.getUserId())
                            .detail(refundLog.generateRefundDescription())
                            .amount(refundLog.getPrice())
                            .orderSn(refundLog.getOrderSn())
                            .serviceType(WalletServiceTypeEnum.REFUND)
                            .outTradeNo(refundLog.getOutTradeNo())
                            .platformWalletId(PlatformWalletEnum.BALANCE.getWalletUserId())
                            .build()
            );

        } catch (Exception e) {
            log.error("线下收款错误", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultMessage<Object> nativePay(PaymentWakeupParam paymentWakeupParam) {
        //同一个会员如果在不同的客户端使用预存款支付，会存在同时支付，无法保证预存款的正确性，所以对会员加锁
        RLock lock = redisson.getLock(paymentWakeupParam.getPayParam().getCombineSn());
        lock.lock();

        try {
            PayParam payParam = paymentWakeupParam.getPayParam();

            //支付发起交易号
            String outTradeNo = SnowFlake.getIdStr();
            //三方流水号
            String transactionId = SnowFlake.getIdStr();

            if (Boolean.TRUE.equals(paymentWakeupParam.getIsCombine())) {
                CombinePaymentLog combinePaymentLog = paymentWakeupParam.getCombinePaymentLog();
                combinePaymentLog.setCombineOutTradeNo(outTradeNo);
                //标记已支付，且已校验
                combinePaymentLog.setIsCheck(true);

                paymentWakeupParam.getPaymentLogs().forEach(paymentLog -> {
                    paymentLog.setOutTradeNo(outTradeNo);
                    paymentLog.setTransactionId(transactionId);
                    //标记已支付，且已校验
                    paymentLog.setIsPay(true);
                    paymentLog.setIsCheck(true);
                });
            } else {

                PaymentLog paymentLog = paymentWakeupParam.getPaymentLog();

                paymentLog.setOutTradeNo(outTradeNo);
                paymentLog.setTransactionId(transactionId);
                //标记已支付，且已校验
                paymentLog.setIsPay(true);
                paymentLog.setIsCheck(true);

            }

            // 回调&发送MQ
            PaymentCallback paymentCallback =
                    Boolean.TRUE.equals(payParam.getIsCombine()) ?
                            PaymentCallback.builder()
                                    .paymentLogs(paymentWakeupParam.getPaymentLogs())
                                    .combinePaymentLog(paymentWakeupParam.getCombinePaymentLog())
                                    .isCombine(true)
                                    .build() :
                            PaymentCallback.builder()
                                    .paymentLog(paymentWakeupParam.getPaymentLog())
                                    .isCombine(false)
                                    .build();
            log.info("线下收款回调");
            applicationEventPublisher.publishEvent(
                    TransactionCommitSendMQEvent.builder()
                            .source("线下收款回调")
                            .exchange(amqpExchangeProperties.getPayment())
                            .routingKey(PaymentRoutingKey.PAYMENT_CALLBACK)
                            .message(paymentCallback)
                            .build());

            return ResultUtil.success();
        } finally {
            lock.unlock();
        }


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OutOrderLogVO profitSharing(ProfitSharingRequestDTO profitSharingRequestDTO) {

        //获取支付日志
        PaymentLog paymentLog =
                paymentLogService.getPaymentLogByOrderSN(profitSharingRequestDTO.getOrderSn());
        if (paymentLog == null) {
            orderClient.updateProfitSharingOrderFlow(profitSharingRequestDTO.getOrderSn());
            orderClient.addOrderFlowProfitSharing(profitSharingRequestDTO.getOrderSn(),"支付日志不存在");
            throw new ServiceException(ResultCode.PROFIT_SHARING_ERROR, "支付日志不存在");
        }

        OutOrderLog outOrderLog = outOrderLogService.getByOrderSn(profitSharingRequestDTO.getOrderSn());
        if (null == outOrderLog) {
            outOrderLog = OutOrderLog.builder()
                    .orderSn(profitSharingRequestDTO.getOrderSn())
                    .amount(profitSharingRequestDTO.getTotalAmount())
                    .paymentMethod(PaymentMethodEnum.BANK_TRANSFER.name())
                    .status(OutOrderLogStatusEnums.FINISHED.name())
                    .outOrderNo(SnowFlake.createStr("OOL"))
                    .build();

            outOrderLogService.save(outOrderLog);
        }

        //保存分账子单记录
        List<OutOrderDetailLog> outOrderDetailLogs = new ArrayList<>();
        for (OutOrderItem outOrderItem : profitSharingRequestDTO.getOutOrderItems()) {
            OutOrderDetailLog outOrderDetailLog = new OutOrderDetailLog(profitSharingRequestDTO.getOrderSn(), outOrderLog.getOutOrderNo(),
                    outOrderItem);
            outOrderDetailLogs.add(outOrderDetailLog);
        }

        //保存分账子账单信息
        outOrderDetailLogService.saveBatch(outOrderDetailLogs);

        walletService.profitSharing(profitSharingRequestDTO);
        return new OutOrderLogVO(outOrderLog, outOrderDetailLogs);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OutOrderLog profitSharingPlatform(PaymentLog paymentLog) {
        //分账记录
        OutOrderLog outOrderLog = outOrderLogService.getByOrderSn(paymentLog.getOrderSn());
        if (null == outOrderLog) {
            outOrderLog = OutOrderLog.builder()
                    .orderSn(paymentLog.getOrderSn())
                    .amount(paymentLog.getPrice())
                    .paymentMethod(PaymentMethodEnum.BANK_TRANSFER.name())
                    .status(OutOrderLogStatusEnums.FINISHED.name())
                    .outOrderNo(SnowFlake.createStr("OOL"))
                    .build();

            outOrderLogService.save(outOrderLog);
        }

        //保存分账子单记录
        OutOrderDetailLog outOrderDetailLog = OutOrderDetailLog.builder()
                .orderSn(paymentLog.getOrderSn())
                .outOrderNo(outOrderLog.getOutOrderNo())
                .amount(paymentLog.getPrice())
                .description(OutOrderDetailLog.generateDescriptionText(paymentLog.getOrderSn(), paymentLog.getPrice()))
                .subMchid(PlatformWalletEnum.BALANCE.getWalletUserId())
                .subMchName(PlatformWalletEnum.BALANCE.getDescription())
                .build();
        //保存分账子账单信息
        outOrderDetailLogService.save(outOrderDetailLog);
        return outOrderLog;
    }
}
